import {
  AbilityBuilder,
  AbilityTuple,
  buildMongoQueryMatcher,
  createMongoAbility,
  MatchConditions,
  MongoAbility,
  PureAbility,
} from '@casl/ability';

import {
  $nor,
  allInterpreters,
  allParsingInstructions,
  MongoQuery,
  nor,
} from '@ucast/mongo2js';
export interface IPolicy {
  type: number; // 0-json,1-mongo,2-function
  effect: 'can' | 'cannot'; // 判断逻辑
  action: string; // CRUD
  subject: string; // Class
  fields?: string[] | string; //字段
  conditions?: string | Record<string, any>; // 查询条件
  args?: string[] | string;
}
type AppAbility = PureAbility<AbilityTuple, MatchConditions>;
type AbilityType = MongoAbility<AbilityTuple, MongoQuery> | AppAbility;
export class CaslAbilityService {
  async buildAbility(polices: IPolicy[], args?: any) {
    const abilityArr: AbilityType[] = [];
    let ability: AbilityType;
    polices.forEach((policy) => {
      switch (policy.type) {
        case 0:
          ability = this.handleJsonType(policy);
          break;
        case 1:
          ability = this.handleMongoType(policy);
          break;
        case 2:
          ability = this.handleFunctionType(policy, args);
          break;
        default:
          ability = this.handleJsonType(policy);
          break;
      }
      abilityArr.push(ability);
    });
    return abilityArr;
  }
  determineAction(offect: string, builder: any) {
    return offect === 'can' ? builder.can : builder.cannot;
  }
  //一般场景
  handleJsonType(policy: IPolicy) {
    const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
    const action = this.determineAction(policy.effect, { can, cannot });

    const localArgs: any = [];
    if (policy.fields) {
      localArgs.push(policy.fields);
    }
    if (policy.conditions) {
      localArgs.push(
        typeof policy.conditions === 'object' && policy.conditions['data']
          ? policy.conditions['data']
          : policy.conditions,
      );
    }

    // subject -> class类的实例
    action(policy.action, policy.subject, ...localArgs);

    // action(policy.action, policy.subject, conditions);
    // action(policy.action, policy.subject, policy.fields);
    // action(policy.action, policy.subject, policy.fields, conditions);
    return build();
  }
  // 针对mongo
  handleMongoType(policy: IPolicy) {
    const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
    const action = this.determineAction(policy.effect, { can, cannot });
    const conditionsMatcher = buildMongoQueryMatcher(
      allParsingInstructions,
      allInterpreters,
    );
    const localArgs: any = [];
    if (policy.fields) {
      localArgs.push(policy.fields);
    }
    if (policy.conditions) {
      localArgs.push(
        typeof policy.conditions === 'object' && policy.conditions['data']
          ? policy.conditions['data']
          : policy.conditions,
      );
    }

    // subject -> class类的实例
    action(policy.action, policy.subject, ...localArgs);

    return build({
      conditionsMatcher,
    });
  }
  // 针对函数
  handleFunctionType(policy: IPolicy, args?: any) {
    const { can, cannot, build } = new AbilityBuilder<AppAbility>(PureAbility);
    const lambdaMatcher = (matchConditions: MatchConditions) => matchConditions;
    const action = this.determineAction(policy.effect, { can, cannot });
    let func: any;
    if (policy.args && policy.args.length) {
      if (policy.conditions && policy.conditions['data']) {
        func = new Function(
          ...policy.args,
          'return ' + policy.conditions['data'],
        );
      } else {
        func = new Function('return ' + policy.conditions);
      }
    } else {
      func = new Function('return ' + policy.conditions);
    }
    action(policy.action, policy.subject, func(...args));
    return build({
      conditionsMatcher: lambdaMatcher,
    });
  }
  // async register(user: any) {
  //   type AppAbility = PureAbility<AbilityTuple, MatchConditions>;
  //   const lambdaMatcher = (matchConditions: MatchConditions) => matchConditions;
  //   const { can, build } = new AbilityBuilder<AppAbility>(PureAbility);
  //   const functionString = `({ authorId }) => authorId === user.id`;
  //   const func = new Function('user', `return ${functionString}`);
  //   can('read', 'Article', func(user));

  //   return build({ conditionsMatcher: lambdaMatcher });
  // }
  // async mongo(user: any) {
  //   const { can, build } = new AbilityBuilder(createMongoAbility);
  //   const conditionsMatcher = buildMongoQueryMatcher(
  //     allParsingInstructions,
  //     allInterpreters,
  //   );
  //   const str = JSON.stringify({
  //     $or: [{ private: true }, { authorId: user.id }],
  //   });
  //   can('read', 'Article', JSON.parse(str));

  //   return build({ conditionsMatcher });
  // }
  // async register1() {
  //   const { can, rules } = new AbilityBuilder(createMongoAbility);

  //   can('read', 'Article');
  //   can('update', 'Article', ['title', 'description'], { authorId: 11 });

  //   return createMongoAbility(rules);
  // }
}
